#include "Common/Utility/ServiceUtility.h"
#include "Common/NetPack/NetPack.h"
#include "base/core/os/debug.h"
#include "base/core/lua_parser.h"
#include "base/core/char_operation.h"
#include "ProtoBuffer/AllProtocol.h"

using namespace ProtoBuf;

int SubNetPack(const char* data, int len, int maxLen, int maxCmd) 
{
    MMO_ASSERT(data);

    if ((unsigned)len >= sizeof(NetPack)) 
	{
        const NetPack* pack = reinterpret_cast<const NetPack*>(data);
        if (pack->GetLength() > (uint32_t)maxLen ||
            pack->GetLength() == 0 /*||*/
            /*pack->GetCmd() >= maxCmd*/) 
		{
			LogErrFmtPrint("package over max, packageSize:%d, max:%d, cmd:%d, module:%d", pack->GetLength(), maxLen, pack->GetCmd(), pack->GetModule());
            return -1;
        }
        if (len >= (int)pack->GetLength()) 
		{
            return (int)pack->GetLength();
        }
    }
    return 0;
}

int SubClientNetPack(const char* data, int len) 
{
	return SubNetPack(data, len, (int)MAX_CLIENT_NET_PACK_LENGTH, 0/*(int)CLIENT_TO_LOGIC_PROTOCOL_COUNT*/);
}

int SubServerNetPack(const char* data, int len) 
{
    return SubNetPack(data, len, (int)MAX_SERVER_NET_PACK_LENGTH, 0/*(int)NET_PROTOCOL_SERVER_TO_CLIENT_COUNT*/);
}

bool ConnectService(Net* net, ServiceConnect* connect, time_t timeInSecond, OnConnectFunc callback, void* context) 
{
    bool result  = false;
    bool retCode = false;
    ConnectPeer peer;

    MMOLOG_ASSERT_EXIT(net != NULL);
    MMOLOG_ASSERT_EXIT(connect != NULL);

    if ((connect->connectState != CONNECT_STATE_READY) || (timeInSecond < connect->connectTime + 4)) 
	{
        goto Exit1;
    }

    peer.ip = connect->remoteIp;
    peer.port = connect->remotePort;
    peer.req = (void*)callback;
    peer.context = context;
    peer.sub_func = SubServerNetPack;
    peer.recvBufSize = INNER_RECVBUF_DEFAULT_SIZE;
    peer.sendBufSize = INNER_SENDBUF_DEFAULT_SIZE;

    retCode = net->Connect(&peer);
    if (retCode) 
	{
        connect->connectState = CONNECT_STATE_CONNECTING;
		connect->connectTime = timeInSecond;
    } else 
	{
		connect->connectTime = timeInSecond;
        LogErrFmtPrint("connect server(%s:%d) err", connect->remoteIp, connect->remotePort);
        goto Exit0;
    }

Exit1:
    result = true;
Exit0:
    return result;
}

bool CloseServerConnection(Net* net, ServiceConnect *connect) 
{
    bool result = false;

    MMOLOG_ASSERT_EXIT(net != NULL);
    MMOLOG_ASSERT_EXIT(connect != NULL);

    if (connect->session != NULL) 
	{
        net->Close(connect->session);
        connect->session = NULL;
        connect->connectState = CONNECT_STATE_BECLOSED;
        LogInfoFmtPrint("server session(%s:%d) close", connect->remoteIp, connect->remotePort);
    }

    result = true;
Exit0:
    return result;
}

bool SerializeProtobufToArray(const ProtoBufBase* pb, void* data, int size) 
{
    bool result = false;
    bool retCode = false;
    int length  = 0;

    MMOLOG_ASSERT_EXIT(pb);
    MMOLOG_ASSERT_EXIT(data);

    if (!pb->IsInitialized()) 
	{
        LogErrFmtPrint("%s require filed \"%s\" not set", pb->GetTypeName().c_str(), pb->InitializationErrorString().c_str());
        //MMOLOG_ASSERT_EXIT(false);
    }

    length = pb->ByteSize();
    MMOLOG_ASSERT_EXIT(length <= size);

    try
	{
        retCode = pb->SerializeToArray(data, size);
        MMOLOG_ASSERT_EXIT(retCode);
    } 
	catch (std::exception* e) 
	{
        if (e != NULL) 
		{
            LogErrFmtPrint("SerializeProtobufToArray error : %s", e->what());
        }
        goto Exit0;
    }

    result = true;
Exit0:
    return result;
}

bool LoadServiceConnectConfig(LuaParser& parser, const char *configName, ServiceConnect *connect) 
{
    bool result = false;
    bool retCode = false;

    MMOLOG_ASSERT_EXIT(configName && connect);
    {
        LuaRef config = parser.GetRef(configName);
        MMOLOG_PROCESS_ERROR(config.isTable());

        retCode = GetLuaTableValue(config, "ID", &connect->id);
        MMOLOG_PROCESS_ERROR(retCode);

        retCode = GetLuaTableValue(config, "IP", connect->remoteIp, sizeof(connect->remoteIp));
        MMOLOG_PROCESS_ERROR(retCode);

        retCode = GetLuaTableValue(config, "Port", &connect->remotePort);
        MMOLOG_PROCESS_ERROR(retCode);
    }
    result = true;
Exit0:
    return result;
}

bool LoadConfigConnect2Service(LuaRef& parserRef, const char *configName, ServiceConnect *connect) 
{
    bool result = false;
    bool retCode = false;

    MMOLOG_ASSERT_EXIT(configName && connect);
    {
		LuaRef  config = parserRef[configName];
		MMOLOG_PROCESS_ERROR(config.isTable());

        retCode = GetLuaTableValue(config, "ID", &connect->id);
        MMOLOG_PROCESS_ERROR(retCode);

        retCode = GetLuaTableValue(config, "IP", connect->remoteIp, sizeof(connect->remoteIp));
        MMOLOG_PROCESS_ERROR(retCode);

        retCode = GetLuaTableValue(config, "Port", &connect->remotePort);
        MMOLOG_PROCESS_ERROR(retCode);
    }
    result = true;
Exit0:
    return result;
}

bool LoadConfigConnectMap2Service(LuaRef& parserRef, const char *configName, CONNECT_MAP& conMap)
{
    bool result = false;
    bool retCode = false;

    MMOLOG_ASSERT_EXIT(configName);
    {
		LuaRef  config = parserRef[configName];
		MMOLOG_PROCESS_ERROR(config.isTable());
		MMOLOG_PROCESS_ERROR(config.length() > 0);

		int count = config.length();
		uint32_t gateId = 0;
		for (int i = 0; i < count; ++i)
		{
			 LuaRef gateConfig = config[i + 1];
			 retCode = GetLuaTableValue(gateConfig, "ID", &gateId);
			 MMOLOG_PROCESS_ERROR(retCode);

			 ServiceConnect& gateConn = conMap[gateId];
			 gateConn.id = gateId;
			 retCode = GetLuaTableValue(gateConfig, "IP", gateConn.remoteIp, sizeof(gateConn.remoteIp));
			 MMOLOG_PROCESS_ERROR(retCode);

			 retCode = GetLuaTableValue(gateConfig, "Port", &gateConn.remotePort);
			 MMOLOG_PROCESS_ERROR(retCode);

			 gateConn.connectState = CONNECT_STATE_READY;
		}
    }
    result = true;
Exit0:
    return result;
}

bool LoadMySQLParam(LuaParser& parser, const char* configName, MYSQL_SERVER_PARAM* param) 
{
    bool result = false;
    bool retCode = false;

    const LuaRef config = parser.GetRef(configName);
    MMOLOG_PROCESS_ERROR(config.isTable());

    retCode = GetLuaTableValue(config, "IP", &param->ip);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Port", &param->port);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Password", &param->password);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "UserName", &param->user);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Database", &param->database);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "characterSet", &param->characterSet);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "FlushFrequency", &param->flushFrequency);
    MMOLOG_PROCESS_ERROR(retCode);

    result = true;
Exit0:
    return result;
}

ScriptPack* GenerateScriptPack(LuaScript* sc, int stackIndex) 
{
    static unsigned char buff[40 * 1024];
    bool        result  = false;
    int         len     = 0;
    ScriptPack* pack    = (ScriptPack*)buff;

    memset(pack, 0, sizeof(ScriptPack));
    len = sc->SaveValue2Buffer(&pack->scriptBuff[0], countof(buff) - sizeof(ScriptPack), stackIndex);
    MMOLOG_PROCESS_ERROR(len > 0);

    pack->SetLength(sizeof(ScriptPack) + len - 1);
    result = true;
Exit0:
    if (!result) 
	{
        pack = nullptr;
    }
    return pack; 
}

bool LoadMySQLParam(LuaRef& parser, MYSQL_SERVER_PARAM* param) 
{
    bool result = false;
    bool retCode = false;

	const LuaRef config = parser["Mysql"];
    MMOLOG_PROCESS_ERROR(config.isTable());

    retCode = GetLuaTableValue(config, "IP", &param->ip);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Port", &param->port);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Password", &param->password);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "UserName", &param->user);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "DataBase", &param->database);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "FlushFrequency", &param->flushFrequency);
    MMOLOG_PROCESS_ERROR(retCode);

    result = true;
Exit0:
    return result;
}

bool LoadRedisParam(LuaRef& parser, REDIS_SERVER_PARAM* param)
{
    bool result = false;
    bool retCode = false;

    const LuaRef config = parser["Redis"];
    MMOLOG_PROCESS_ERROR(config.isTable());

    retCode = GetLuaTableValue(config, "IP", &param->ip);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Port", &param->port);
    MMOLOG_PROCESS_ERROR(retCode);

    retCode = GetLuaTableValue(config, "Password", &param->password);
    MMOLOG_PROCESS_ERROR(retCode);

    result = true;
Exit0:
    return result;
}
